Amazon.Lambda.AspNetCoreServer を使ったときに、HTTP リクエストコンテキストを取得してみた
いわさです。
Lambda 関数では、イベント情報を受信して、そこからHTTPリクエスト情報などを取得することが出来ます。
通常の Lambda 関数であればハンドラの引数の専用オブジェクトへアクセスすれば良さそうです。
一方で、前回 ASP.NET Core の標準実装した Web API をそのまま Lambda 関数としてデプロイする方法をご紹介しました。
デフォルトテンプレートではリクエスト情報などへのアクセスはしていないシンプルな Hello World でした。
この場合、HTTPリクエスト情報などはどのように取得したら良いでしょうか。
Amazon.Lambda.AspNetCoreServer を使わない Lambda
まずは、Amazon.Lambda.AspNetCoreServer
を使用しない、Lambda 関数にあわせたハンドラを使った場合をおさらいします。
冒頭に引用した前回の記事で、Amazon.Lambda.Templates
を導入する方法をご紹介していました。
そのテンプレートのひとつにEmpty Top-level Function
というものがありますので、こちらを使ってみましょう。
% dotnet new lambda.EmptyTopLevelFunction テンプレート "Empty Top-level Function" が正常に作成されました。
ハンドラはモジュールを指していますが、LambdaBootstrapBuilder
によって内部でハンドラを生成して渡されています。
以下ではデフォルトinputがstring
だとエラーが発生したため、object
に変更しています。
using Amazon.Lambda.Core; using Amazon.Lambda.RuntimeSupport; using Amazon.Lambda.Serialization.SystemTextJson; // The function handler that will be called for each Lambda event var handler = (object input, ILambdaContext context) => { LambdaLogger.Log(context.ClientContext.ToString()); LambdaLogger.Log(input.ToString()); return "success"; }; // Build the Lambda runtime client passing in the handler to call for each // event and the JSON serializer to use for translating Lambda JSON documents // to .NET types. await LambdaBootstrapBuilder.Create(handler, new DefaultLambdaJsonSerializer()) .Build() .RunAsync();
上記をデプロイし、API Gateway などからHTTPリクエストを中継すると、input
の中身は以下のようになります。
ここではリクエスト時にhoge
ヘッダーを付与しています。
2022-07-17T22:45:41.587Z 1185bb86-a4ef-4bf3-8d2e-70cf1721478f { "version": "2.0", "routeKey": "ANY /hoge0717lambdadotnet", "rawPath": "/hoge0717lambdadotnet", "rawQueryString": "", "headers": { "accept": "*/*", "content-length": "0", "hoge": "111", "host": "2sewhgz0sb.execute-api.ap-northeast-1.amazonaws.com", "user-agent": "curl/7.79.1", "x-forwarded-for": "", "x-forwarded-port": "443", "x-forwarded-proto": "https" }, "requestContext": { "accountId": "", "apiId": "2sewhgz0sb", "domainName": "2sewhgz0sb.execute-api.ap-northeast-1.amazonaws.com", "domainPrefix": "2sewhgz0sb", "http": { "method": "GET", "path": "/hoge0717lambdadotnet", "protocol": "HTTP/1.1", "sourceIp": "", "userAgent": "curl/7.79.1" }, "requestId": "VbubUjdXtjMEMdg=", "routeKey": "ANY /hoge0717lambdadotnet", "stage": "$default", "time": "17/Jul/2022:22:45:41 +0000", "timeEpoch": 1658097941004 }, "isBase64Encoded": false }
このように、Lambda 用のリクエストオブジェクトを解析して必要なリクエストコンテキストを取得していたかと思います。
Amazon.Lambda.AspNetCoreServer を使う Lambda
では、Amazon.Lambda.AspNetCoreServer
を使うとどうなるでしょうか。
Amazon.Lambda.AspNetCoreServer
を使う場合は Lambda 用にリファクタリングせずに標準の Web API をそのまま Lambda 化出来るのがメリットなので、Lambda 専用の実装はできれば避けたいところでしょう。
そして、Amazon.Lambda.AspNetCoreServer
は標準実装と Lambda 実装(API Gateway / Application Load Balancer) の間をうまいことやってくれる思想なので、コンテキストまわりもうまいことやってくれる予感がしますね。
ということで、ASP.NET Core 標準の方法でヘッダー情報へアクセスしてみましょう。
こちらも、Amazon.Lambda.Templates
から Minimal API を作成します。
% dotnet new serverless.AspNetCoreMinimalAPI テンプレート "Lambda ASP.NET Core Minimal API" が正常に作成されました。
マッピングされたラムダ式の引数にまずはシンプルにHttpRequest
を指定します。
なお、実装方法は以下公式ドキュメントの「パラメータバインド」を参考にしています。
: app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.MapGet("/", (HttpRequest request) => { return request.Headers["x-hoge"][0]; }); app.Run();
Minimal API で Lambda テンプレートを作成すると、SAMテンプレートも自動生成されます。
そして、dotnet lambda deploy-serverless
を実行すると API Gateway 含めてデプロイされます。
% dotnet lambda deploy-serverless --profile hoge Amazon Lambda Tools for .NET Core applications (5.4.4) Project Home: https://github.com/aws/aws-extensions-for-dotnet-cli, https://github.com/aws/aws-lambda-dotnet : Output Name Value ------------------------------ -------------------------------------------------- ApiURL https://3nflaanqkk.execute-api.ap-northeast-1.amazonaws.com/Prod/
アクセスしてみましょう。
% curl https://3nflaanqkk.execute-api.ap-northeast-1.amazonaws.com/Prod/ -H "x-hoge:hogefuga" hogefuga
おお、うまいこと使えますね!!
直接パラメータバインドも出来るか試してみます。
まぁここまで出来れば出来そうだなという感じではあります。
using Microsoft.AspNetCore.Mvc; var builder = WebApplication.CreateBuilder(args); : app.MapGet("/", ([FromHeader(Name = "x-hoge")] string hoge) => { return hoge; }); app.Run();
% curl https://3nflaanqkk.execute-api.ap-northeast-1.amazonaws.com/Prod/ -H "x-hoge:hogehoge" hogehoge
おお、こちらもいけました。すごい。
さいごに
本日は、Amazon.Lambda.AspNetCoreServer
を使って ASP.NET Core Web API を Lambda 関数としてデプロイした時に、リクエストコンテキストを取得してみました。
結果としては、Lambda でホスティングされていることを意識せずにHTTPリクエストへアクセスする実装を行うことが出来ました。
これはとても良いですね。
Lambda 用にリファクタリングせずに ASP.NET Core Web API を サーバーレス環境でホスティング出来そうです。
まだオーバーヘッド部分などを無視しているので、色々考慮事項などは出てきそうな気がしますが、.NET も Lambda でのメジャーな選択肢として考えても良さそうな気がしますね。
なお、色々うまいこと抽象化してくれて使いやすくなってますが、Lambda のコンテキストにもアクセスすることは可能です。
使わないほうが綺麗な実装になる気はしますが、アクセス出来なくも無いということだけ覚えておきたいです。
The original lambda request object and the ILambdaContext object can be accessed from the HttpContext.Items collection.
AWS Toolkit for Visual Studio の無い環境だと、.NET on Lambda に対する敷居が少し高かったのですが、今は以下の2つのコマンドだけで Lambda に依存しない .NET な Web API をすぐデプロイ出来るのでかなりお手軽です。.NET 使いの方は是非試していただきたいですね。
dotnet new serverless.AspNetCoreMinimalAPI
dotnet lambda deploy-serverless